﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using HQPad.Models;
using HQPad.DAL;

namespace HQPad.Services
{
    /// <summary>Sphere types.</summary>
    public enum Sphere
    {
        Good,
        Neutral,
        Evil
    }

    public interface IInvocationService
    {
        Invocation AddInvocation(int ruleSetId, string slug, string name, string description, string range, string duration, bool extendable, int rank, Sphere sphere, bool isCommon, string groupName, string family);
        Invocation GetInvocation(int invocationId);
        Invocation GetInvocationByLegacy(string legacyName, int ruleSetId);
        Invocation GetInvocation(int ruleSetId, string slug);
        IEnumerable<AvailableInvocation_Result> GetAllowedInvocations(int ruleSetID, string knownInvocations, string guilds, string maxRanks);
        IEnumerable<Invocation> GetInvocations(int ruleSetId);
        IEnumerable<Invocation> GetInvocationsByGroup(int ruleSetId, string groupSlug);
        IEnumerable<Invocation> GetInvocationsByRequiredGroup(int ruleSetId, string requiredGroupSlug);
        void UpdateInvocation(Invocation invocation);
        void DeleteInvocation(int invocationId);
        string CreateInvocationSlug(string invocationName);
        void Add(Invocation invocation);
        void AddGuild(int invocationId, Guild guild);
    }

    public class InvocationService : IInvocationService
    {
        private HQContext context = null;

        public InvocationService(IUnitOfWork context)
        {
            this.context = (HQContext)context;
        }

        #region AddInvocation
        /// <summary>Add a new invocation record.</summary>
        /// <param name="ruleSetId">The parent rule set.</param>
        /// <param name="slug">A unique url slug for the invocation.</param>
        /// <param name="name">The invocation name.</param>
        /// <param name="description">The invocation description.</param>
        /// <param name="range">The invocation range.</param>
        /// <param name="duration">The invocation duration.</param>
        /// <param name="extendable">Whether the invocation is extendable.</param>
        /// <param name="level">The invocation casting rank.</param>
        /// <param name="colour">The invocation sphere.</param>
        /// <param name="isCommon">Whether the spell is a common invocation.</param>
        /// <returns></returns>
        public Invocation AddInvocation(int ruleSetId, string slug, string name, string description, string range, string duration,
            bool extendable, int rank, Sphere sphere, bool isCommon, string groupName, string family)
        {
            Invocation invocation = new Invocation()
                {
                    RuleSetID = ruleSetId,
                    Sphere = Enum.GetName(typeof(Sphere), sphere),
                    Deleted = false,
                    LastUpdatedByUserID = 1,
                    LastUpdated = DateTime.Now,
                    Name = name,
                    Description = description,
                    Range = range,
                    Duration = duration,
                    IsExtendable = extendable,
                    Rank = rank,
                    Slug = slug,
                    IsCommon = isCommon,
                    GroupName = groupName,
                    Family = family,
                    GroupSlug = CreateInvocationSlug(groupName)
                };

            context.Invocations.Add(invocation);

            return invocation;
        }
        #endregion

        #region GetInvocation
        /// <summary>Get a invocation by it's unique ID.</summary>
        /// <param name="invocationId">A unique invocation ID.</param>
        /// <returns>The spell with the unique invocation ID or null.</returns>
        public Invocation GetInvocation(int invocationId)
        {
            return context.Invocations.Find(invocationId);
        }

        /// <summary>Get a invocation by it's unique url slug within a rule set.</summary>
        /// <param name="ruleSetId">A rule set ID.</param>
        /// <param name="slug">A invocation url slug, unique within a rule set.</param>
        /// <returns>The invocation found or null.</returns>
        public Invocation GetInvocation(int ruleSetId, string slug)
        {
            return context.Invocations
                .Where(s => s.RuleSetID == ruleSetId && s.Slug.ToLower() == slug.ToLower())
                .FirstOrDefault();
        }
        #endregion

        #region GetInvocationByLegacy
        /// <summary>Get a invocation by it's legacy name within a rule set.</summary>
        /// <param name="ruleSetId">A rule set ID.</param>
        /// <param name="slug">A invocation legacy name.</param>
        /// <returns>The invocation found or null.</returns>
        public Invocation GetInvocationByLegacy(string legacyName, int ruleSetId)
        {
            return context.Invocations
                .Where(s => s.RuleSetID == ruleSetId && s.GroupName.ToLower() == legacyName.ToLower())
                .FirstOrDefault();
        }
        #endregion

        #region GetInvocations
        /// <summary>Get all invocations in a rule set.</summary>
        /// <param name="ruleSetId">A rule set ID.</param>
        /// <returns>All invocations in a rule set id sorted by sphere, invocation name and level.</returns>
        public IEnumerable<Invocation> GetInvocations(int ruleSetId)
        {
            return from s in context.Invocations
                   where s.RuleSetID == ruleSetId
                   orderby s.Sphere , s.GroupName ascending , s.Rank ascending
                   select s;
        }
        #endregion

        #region UpdateInvocation
        /// <summary>Mark a invocation as updated so that is it saved to db.</summary>
        /// <param name="invocation">The invocation to update.</param>
        public void UpdateInvocation(Invocation invocation)
        {
            context.Entry(invocation).State = System.Data.EntityState.Modified;
        }
        #endregion

        #region GetInvocationsByGroup
        /// <summary>
        /// Get all invocations by group slug.
        /// </summary>
        /// <param name="ruleSetId">A rule set ID.</param>
        /// <param name="groupSlug">An invocation group slug such as "cure-25"</param>
        /// <returns>All invocations in the group.</returns>
        public IEnumerable<Invocation> GetInvocationsByGroup(int ruleSetId, string groupSlug)
        {
            return from s in context.Invocations
                   where s.RuleSetID == ruleSetId && s.GroupSlug == groupSlug
                   orderby s.Sphere, s.GroupName ascending, s.Rank ascending
                   select s;
        }
        #endregion

        #region GetInvocationsByRequiredGroup
        /// <summary>
        /// Get all invocations that have a requirement on a group name (slug)
        /// </summary>
        /// <param name="ruleSetId">A rule set ID.</param>
        /// <param name="requiredGroupSlug">An invocation group slug such as "cure-25"</param>
        /// <returns>All invocations with this group as a requirement.</returns>
        public IEnumerable<Invocation> GetInvocationsByRequiredGroup(int ruleSetId, string requiredGroupSlug)
        {
            return from s in context.Invocations
                   where s.RuleSetID == ruleSetId && s.RequiredGroupName == requiredGroupSlug
                   orderby s.Sphere, s.GroupName ascending, s.Rank ascending
                   select s;
        }
        #endregion

        #region DeleteInvocation
        /// <summary>Delete a invocation.</summary>
        /// <param name="invocationId">The unique id of the invocation to delete.</param>
        public void DeleteInvocation(int invocationId)
        {
            Invocation invocation = GetInvocation(invocationId);
            context.Entry(invocation).State = System.Data.EntityState.Deleted;
        }
        #endregion

        #region CreateInvocationSlug
        /// <summary>Create a invocation slug from a invocation name.</summary>
        /// <param name="invocationName">The name of the invocation</param>
        /// <returns>The standard format skill slug.</returns>
        public string CreateInvocationSlug(string invocationName)
        {
            return invocationName
                .ToLower()
                .Replace("_", "")
                .Replace("-", "")
                .Replace("&", "")
                .Replace("+", "")
                .Replace(",", "")
                .Replace(".", "")
                .Replace(" ", "-")
                .Replace("--", "-")
                .Replace("--", "-")
                .Replace("'", "");
        }
        #endregion

        #region Add
        /// <summary>Add a invocation object to the database.</summary>
        /// <param name="invocation">The invocation objetc to add.</param>
        public void Add(Invocation invocation)
        {
            context.Invocations.Add(invocation);
        }
        #endregion

        #region AddGuild
        /// <summary>
        /// Add a guild reference to an invocation.
        /// </summary>
        /// <param name="invocationId"></param>
        /// <param name="guild"></param>
        public void AddGuild(int invocationId, Guild guild)
        {
            if (guild != null)
            {
                Invocation parentInvocation = GetInvocation(invocationId);

                if (parentInvocation.Guilds == null)
                    parentInvocation.Guilds = new List<Guild>();

                parentInvocation.Guilds.Add(guild);
            }
        }
        #endregion

        public IEnumerable<AvailableInvocation_Result> GetAllowedInvocations(int ruleSetId, string knownInvocations, string guilds, string maxRanks)
        {
            IEnumerable<AvailableInvocation_Result> allowedInvocation =
                context.Database.SqlQuery<AvailableInvocation_Result>(
                    "[hqpad-character-allowed-invocations] @guilds, @maxRanks, @rulesId, @knownList",
                    new SqlParameter("@guilds", guilds),
                    new SqlParameter("@maxRanks", maxRanks),
                    new SqlParameter("@rulesId", ruleSetId),
                    new SqlParameter("@knownList", knownInvocations));

            return allowedInvocation;
        }
    }
}